const WebSocket = require('ws')
const jwt = require('jsonwebtoken')
const moment = require('moment')
const Message = require('../models/messageModel')
const { redisClient, redisSubscriber } = require('../config/db') // 导入现有的 Redis 客户端
const { mysqlPool } = require('../config/db')

// 创建 WebSocket 服务器
class WebSocketService {
    constructor(server) {
        // 创建 WebSocket 服务
        this.wss = new WebSocket.Server({
            // server,
            // path: '/ws/',
            noServer: true,
            clientTracking: true // 我们不使用内置的客户端跟踪
        })

        this.connections = new Map() // 存储所有活跃连接 (本地)
        this.heartbeatInterval = 30 * 1000 // 定义心跳时间 30 秒
        this.wss.on('connection', this.handleConnection.bind(this)) // 绑定事件处理

        // 订阅 Redis 频道用于跨实例消息传递
        redisSubscriber.subscribe('front:message', (message, channel) => {
            this.handleRedisMessage(channel, message)
        })

        server.on('upgrade', this.handleUpgrade.bind(this)) // 绑定 HTTP 升级处理

        // 心跳检测
        this.startHeartbeat()
    }

    // 处理连接
    async handleConnection(ws, request) {
        const userId = ws.userId

        // 存储连接
        if (!this.connections.has(userId)) {
            this.connections.set(userId, new Set())
        }

        this.connections.get(userId).add(ws)

        // 在 Redis 中记录用户连接 (用于分布式环境)
        await redisClient.sAdd(`user:${userId}:connections`, ws._socket.remoteAddress)

        // 设置心跳
        ws.isAlive = true
        ws.on('pong', () => {
            ws.isAlive = true
        })

        // 处理消息
        ws.on('message', (event) => {
            this.handleMessage(ws, event)
        })

        ws.on('error', (error) => {
            console.error('WebSocket error:', error)
            this.handleClose(ws)
        })

        // 处理连接关闭
        ws.on('close', () => {
            this.handleClose(ws)
        })
    }

    // 处理连接关闭
    async handleClose(ws) {
        const userId = ws.userId

        // 从本地连接映射中移除
        if (this.connections.has(userId)) {
            const userConnections = this.connections.get(userId)
            userConnections.delete(ws)
            
            if (userConnections.size === 0) {
                this.connections.delete(userId)
            }
        }

        // 从 Redis 中移除连接记录
        await redisClient.sRem(`user:${userId}:connections`, ws._socket.remoteAddress)

        console.log('监听到有用户下线，下线用户ID为：' + userId)
    }

    // 处理 HTTP 升级请求
    async handleUpgrade(request, socket, head) {
        try {
            // 从查询参数获取 token
            const url = new URL(request.url, `http://${request.headers.host}`)
            const token = url.searchParams.get('token')

            // 如果token 为空，则返回错误
            if (!token) {
                socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
                socket.destroy()
                return
            }
            
            // 验证 JWT token
            const decoded = jwt.verify(token, process.env.JWT_SECRET)
            const userId = decoded.userId

            // 鉴权失败，给客户端返回 401 错误
            if (!userId) {
                socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
                socket.destroy()
                return
            }

            // 处理 WebSocket 升级
            this.wss.handleUpgrade(request, socket, head, (ws) => {
                // 将用户ID附加到 WebSocket 对象
                ws.userId = userId
                this.wss.emit('connection', ws, request)
            })
        } catch (error) {
            console.error('WebSocket upgrade error:', error)
            socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
            socket.destroy()
        }
    }

    // 发送私信给特定用户
    // 发送者：senderId，uniqueId：唯一标识， 接收者：recipientId，content：消息内容，type：消息类型 1 文本 2 图片
    async sendPrivateMessage({ senderId, uniqueId, recipientId, content, type }) {
        // 获取发送人信息
        const [userInfo] = await mysqlPool.execute('SELECT * FROM users WHERE id = ?', [senderId])

        // 不管对方在不在线，将消息存入到数据库，得到消息 id
        const chatId = await this.storeMessageInDB(senderId, recipientId, content, type)
        const createTime = moment().format('YYYY-MM-DD HH:mm:ss') // 创建时间

        // 创建消息对象
        const messageObj = {
            type: 'chat_send',
            data: {
                chatId, // 消息ID
                senderId, // 发送者ID
                uniqueId, // 唯一标识
                recipientId, // 接收者ID
                content, // 消息内容
                type, // 1 文本 2 图片
                state: 0, // 0 未读，1 已读
                isRevoke: 0, // 0 可以撤回，1 不可撤回
                senderId, // 发送者ID
                senderUsername: userInfo[0].username, // 发送者用户名
                senderAvatar: userInfo[0].avatar, // 发送者头像
                senderNickName: userInfo[0].nickname, // 昵称
                createTime, // 创建时间
                msg: '消息发送成功！',
            }
        }

        // 如果用户不在本地，通过 Redis 发布消息
        if (chatId) {
            redisClient.publish('front:message', JSON.stringify(messageObj))
        }
    }

    // 存储私信到数据库 (需要你的实现)
    async storeMessageInDB(senderId, recipientId, content, type) {
        const state = 0 // 只要发送消息，未读状态就显示未读，而在前端部分，用户进入聊天界面就需要将谁的聊天的未读状态改为已读
        const createTime = moment().format('YYYY-MM-DD HH:mm:ss') // 创建时间

        // 将聊天信息存入数据库中
        const sql = `
            INSERT INTO chat (
                sender_id,
                recipient_id,
                content,
                type,
                state,
                createTime
            )
            VALUES (?, ?, ?, ?, ?, ?)
        `
        const [result] = await mysqlPool.execute(sql, [senderId, recipientId, content, type, state, createTime])

        return result.insertId
    }
    
    // 处理来自 Redis 的消息
    async handleRedisMessage(channel, message) {
        if (channel !== 'front:message') return

        const data = JSON.parse(message)

        if (data.type === 'chat') {
            // 发送私信消息给用户
            const params = {
                senderId: data.userId,
                uniqueId: data.uniqueId,
                recipientId: data.data.recipientId,
                content: data.data.content,
                type: data.data.type, // 1 文本，2 图片
            }

            this.sendPrivateMessage(params)
        } else if (
            data.type === 'follow' ||
            data.type === 'follow_cancel'
        ) {
            // （取消）关注好友
            const msg = await Message.handleFollow(data.type, data.userId, data.data.recipientId)

            if (msg) {
                const messageObj = {
                    type: 'follow_notice',
                    data: { ...msg, recipientId: data.data.recipientId }
                }

                redisClient.publish('front:message', JSON.stringify(messageObj))
            }
        }
    }
    
    // 心跳检测
    startHeartbeat() {
        setInterval(() => {
            // console.log('--------------------------------------------------------->发送心跳包...')
            // console.log('当前自定义连接映射中的用户数量：' + this.connections.size)
            this.connections.forEach((userConnections, userId) => {
                userConnections.forEach(ws => {
                    if (!ws || typeof ws.ping !== 'function') {
                        console.log('WebSocket 实例无效，跳过心跳检测')
                        return
                    }
                    
                    if (ws.isAlive === false) {
                        console.log('心跳超时，终止webSocket连接！')
                        userConnections.delete(ws)
                        ws.terminate()
                        return
                    }
    
                    ws.isAlive = false
                    try {
                        ws.ping()
                    } catch (error) {
                        console.error('Ping error:', error)
                        userConnections.delete(ws)
                    }
                })
            })
        }, this.heartbeatInterval)
    }
}

module.exports = WebSocketService
